package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/olivere/elastic/v7"
	"go.uber.org/zap"
	"gorm.io/gorm/clause"
	"niu-video/app/consts"
	"niu-video/app/model"
	"niu-video/app/repository"
	"niu-video/app/utils"
	"niu-video/config"
	"niu-video/pkg/es"
	"niu-video/pkg/logger"
	"niu-video/pkg/qiniu"
	"niu-video/pkg/rabbitmq"
	"niu-video/pkg/redis"
	"strings"
	"sync"
)

var VideoService = newVideoService()

func newVideoService() *videoService {
	return &videoService{}
}

type videoService struct {
}

func (v *videoService) Publish(request model.PublishVideo, userId int64) error {
	uploadVideo := model.VideoMessage{
		UserID:     userId,
		Title:      request.Title,
		VideoURL:   request.VideoUrl,
		CoverURL:   request.CoverUrl,
		CategoryId: request.CategoryId,
		Tags:       request.Tags,
	}

	uploadVideoJson, err := json.Marshal(uploadVideo)
	if err != nil {
		return errors.New(fmt.Sprintf("json解析失败%v", uploadVideoJson))
	}
	rabbit := rabbitmq.NewRabbitMQ(consts.RABBITMQ_VIDEO_EXCHANGE, consts.RABBITMQ_VIDEO_ROUTE, consts.RABBITMQ_VIDEO_QUEUE)
	defer rabbit.Close()
	rabbit.SendMessage(rabbitmq.Message{Body: string(uploadVideoJson)})

	return nil
}

func (v *videoService) List(request model.PageRequest, userId int64) (videos []model.VideoVo, err error) {
	order := clause.OrderByColumn{
		Column: clause.Column{
			Name: "create_time",
		},
		Desc: true,
	}
	// 分页查询所有
	list, err := repository.VideoRepository.Page(request.Page, request.PageSize, order)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	videos, err = v.concurrencyGetVideos(list, userId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	return
}

func (v *videoService) Category(request model.CategoryQuery, userId int64) (videos []model.VideoVo, err error) {

	order := clause.OrderByColumn{
		Column: clause.Column{
			Name: "create_time",
		},
		Desc: true,
	}
	list := repository.CategoryRepository.GetVideoByCategory(request.Page, request.PageSize, request.CategoryID, order)
	videos, err = v.concurrencyGetVideos(list, userId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	return
}

func (self *videoService) concurrencyGetVideos(list []model.Video, userId int64) (videos []model.VideoVo, err error) {
	// 创建等待组，等待所有查询操作完成
	var wg sync.WaitGroup
	wg.Add(5)

	for _, v := range list {
		vo := model.VideoVo{
			CoverUrl: v.CoverURL,
			VideoID:  v.VideoID,
			VideoURL: v.VideoURL,
			Title:    v.Title,
			UserId:   v.UserID,
			Time:     utils.DateDiff(v.CreateTime),
			Tags:     strings.Split(v.Tags, " "),
		}
		if vo.Tags[0] == "" {
			vo.Tags = []string{}
		}
		videos = append(videos, vo)
	}

	// 查询作者以及是否关注
	go func() {
		for i, v := range videos {
			isFollow := false
			if userId != 0 {
				isFollow = RelationService.IsFollow(v.UserId, userId)
			}
			user := repository.UserRepository.Get(v.UserId)
			videos[i].Author = model.Author{
				UserId:   v.UserId,
				IsFollow: isFollow,
				Username: user.Username,
				Avatar:   user.Avatar,
				Nickname: user.Nickname,
			}
		}
		wg.Done()
	}()

	// 所在分类
	go func() {
		categories := repository.CategoryRepository.All()
		categoryMap := make(map[int64]model.Category)
		for _, category := range categories {
			categoryMap[category.CategoryID] = category
		}
		for i := range videos {
			videos[i].Category = categoryMap[list[i].CategoryId]
		}
		wg.Done()
	}()
	// 评论数
	go func() {
		for i := range videos {
			videos[i].CommentCount = CommentService.CommentCount(list[i].VideoID)
		}
		wg.Done()
	}()
	// 是否收藏
	go func() {
		for i, v := range videos {
			videos[i].CollectCount = CollectService.CollectCount(v.VideoID)
			// 是否收藏
			if userId != 0 {
				videos[i].IsCollect = CollectService.IsCollect(v.VideoID, userId)
			}
		}
		wg.Done()
	}()
	// 是否点赞
	go func() {
		for i, v := range videos {
			videos[i].LikeCount = LikeService.LikeCount(v.VideoID)
			// 是否点赞
			if userId != 0 {
				videos[i].IsLike = LikeService.IsLike(v.VideoID, userId)
			}
		}
		wg.Done()
	}()

	wg.Wait()
	return videos, nil
}

func (v *videoService) Search(request model.PageInfo, userId int64) (videos []model.VideoVo, err error) {

	// 构建搜索查询
	query := elastic.NewBoolQuery().
		Should(
			elastic.NewMatchQuery("title", request.Keyword),
			elastic.NewBoolQuery().Should(
				elastic.NewMatchQuery("tags", request.Keyword),
			),
		)

	// 执行搜索
	searchResult, err := es.ES.SearchDocuments(consts.VIDOE_ES_INDEX, query, request.Page, request.PageSize)
	if err != nil {
		logger.ErrorString("es", "search", err.Error())
		return videos, err
	}
	for _, hit := range searchResult.Hits.Hits {
		var video model.VideoVo
		err := json.Unmarshal(hit.Source, &video)
		video.Time = utils.DateDiff(video.CreateTime)
		logger.LogIf(err)
		videos = append(videos, video)
	}

	var wg sync.WaitGroup
	wg.Add(3)

	// 查询作者以及是否关注
	go func() {
		for i, v := range videos {
			isFollow := false
			if userId != 0 {
				isFollow = RelationService.IsFollow(v.UserId, userId)
			}
			user := repository.UserRepository.Get(v.UserId)
			videos[i].Author = model.Author{
				UserId:   v.UserId,
				IsFollow: isFollow,
				Username: user.Username,
				Avatar:   user.Avatar,
				Nickname: user.Nickname,
			}
		}
		wg.Done()
	}()

	// 是否收藏
	go func() {
		for i, v := range videos {
			videos[i].CollectCount = CollectService.CollectCount(v.VideoID)
			// 是否收藏
			if userId != 0 {
				videos[i].IsCollect = CollectService.IsCollect(v.VideoID, userId)
			}
		}
		wg.Done()
	}()
	// 是否点赞
	go func() {
		for i, v := range videos {
			videos[i].LikeCount = LikeService.LikeCount(v.VideoID)
			// 是否点赞
			if userId != 0 {
				videos[i].IsLike = LikeService.IsLike(v.VideoID, userId)
			}
		}
		wg.Done()
	}()

	wg.Wait()
	return videos, nil

}

// GetUploadToken
//
//	@Description: 获取video上传信息
//	@receiver v
//	@param ticket
//	@return resp
func (v *videoService) GetUploadToken(ticket string) (resp model.VideoUploadTokenResponse) {
	resp.UploadToken = qiniu.GetUploadToken(ticket)
	resp.Domain = config.Conf.Qiniu.Url
	return
}

// UserWork
//
//	@Description: 用户的作品分页列表
//	@receiver v
//	@param request
//	@param userId
//	@return videos
//	@return err
func (v *videoService) UserWork(request model.UserWorkQuery, userId int64) (videos []model.VideoVo, err error) {

	list, err := repository.VideoRepository.UserVideoList(request.Page, request.PageSize, request.UserId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	videos, err = v.concurrencyGetVideos(list, userId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	return
}

func (v *videoService) UserLike(request model.UserWorkQuery, userId int64) (videos []model.VideoVo, err error) {
	if request.UserId != userId {
		return videos, errors.New("不可访问哦！")
	}
	list, err := repository.LikeRepository.LikeUserVideo(request.Page, request.PageSize, request.UserId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	videos, err = v.concurrencyGetVideos(list, userId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	return
}

func (v *videoService) UserCollect(request model.UserWorkQuery, userId int64) (videos []model.VideoVo, err error) {
	if request.UserId != userId {
		return videos, errors.New("不可访问哦！")
	}
	list, err := repository.CollectRepository.CollectUserVideo(request.Page, request.PageSize, request.UserId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	videos, err = v.concurrencyGetVideos(list, userId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	return
}

func (v *videoService) Delete(videoId int64, userId int64) error {
	video := repository.VideoRepository.GetOne(videoId)
	if video.UserID != userId {
		return errors.New("您只能删除您自己的作品哦")
	}
	//  删除作品
	repository.VideoRepository.Delete(videoId)
	// 删除es中的搜索信息
	err := es.ES.DeleteDocument(consts.VIDOE_ES_INDEX, videoId)
	if err != nil {
		logger.Error("es", zap.String("index", err.Error()))
	}

	return nil
}

func (v *videoService) Recommend(request model.PageRequest, userId int64) (videos []model.VideoVo, err error) {
	key := fmt.Sprintf("%s:%d", consts.REDIS_RECOMMEND_PREFIX, userId)
	videoIds := redis.Redis.LRange(
		key,
		int64((request.Page-1)*request.PageSize),
		int64(request.Page*request.PageSize)-1)

	var noView []string
	for _, id := range videoIds {
		if !redis.Redis.SIsMember(fmt.Sprintf("%s:%d", consts.REDIS_USER_VIEW_PREFIX, userId), id) {
			noView = append(noView, id)
		}
	}
	var vs []model.Video
	if len(noView) != 0 {
		vs, err = repository.VideoRepository.In(noView)
		if err != nil {
			logger.Error("video", zap.String("list", "视频列表查询失败"))
			return videos, errors.New("视频列表查询失败")
		}
	}
	if len(vs) < request.PageSize {
		list, err := repository.VideoRepository.Random(request.PageSize - len(vs))
		if err != nil {
			logger.Error("video", zap.String("list", "视频列表查询失败"))
			return videos, errors.New("视频列表查询失败")
		}
		for _, v := range list {
			vs = append(vs, v)
		}
	}
	videos, err = v.concurrencyGetVideos(vs, userId)
	if err != nil {
		logger.Error("video", zap.String("list", "视频列表查询失败"))
		return videos, errors.New("视频列表查询失败")
	}
	return
}
